#include <QMetaType>

#include "qlscustomplot.h"

QLsCustomPlot::QLsCustomPlot(QWidget *parent/* = nullptr*/) :
	QCustomPlot(parent),
	mZoomMode(true),
	mRubberBand(new QRubberBand(QRubberBand::Rectangle, this)),
	m_clickedTimer(new QTimer),
	m_bgImg(nullptr),
	m_bShowPoint(false)							/* 光标划过曲线显示点的坐标 */
{
	/* 图形初始化 */
	PlotInit();

	connect(m_clickedTimer, &QTimer::timeout, this, &QLsCustomPlot::slotClicked);
}

/* 新增图层(QCustom每个线条占据一个图层) */
QCPGraph *QLsCustomPlot::AddGraph(
		QString graphName, QColor graphColor,
		QCPScatterStyle::ScatterShape ssCircle/* = QCPScatterStyle::ssCircle*/)
{
	/* 新增图层 */
	QCPGraph *graph = this->addGraph(this->xAxis, this->yAxis);

	if(graph)
	{
		graph->setName(graphName);						/* 名称 */
		graph->setPen(QPen(graphColor, 2));				/* 颜色 */
		graph->setScatterStyle(							/* 格式 */
					QCPScatterStyle(
						ssCircle,
						QPen(graphColor, 2),
						QBrush(graphColor),
						1
						)
					);
		graph->setLineStyle(QCPGraph::lsLine);			/* 直线 */
	}

	return graph;
}

/* 绘制图像，自动设置绘图范围 */
void QLsCustomPlot::SetData(
		const QVector<double> &key,
		const QVector<double> &value,
		int graphIndex/* = 0*/
		)
{
	QCPRange xRange, yRange;

	xRange.lower = xRange.upper = key[0];
	yRange.lower = yRange.upper = value[0];
	for(int i = 1; i < key.size(); i++)
	{
		xRange.lower = qMin(xRange.lower, key[i]);
		xRange.upper = qMax(xRange.upper, key[i]);
	}
	for(int i = 1; i < value.size(); i++)
	{
		yRange.lower = qMin(yRange.lower, value[i]);
		yRange.upper = qMax(yRange.upper, value[i]);
	}

	xRange.lower -= 0.01 * qFabs(xRange.size());
	xRange.upper += 0.01 * qFabs(xRange.size());
	yRange.lower -= 0.1 * qFabs(yRange.size());
	yRange.upper += 0.1 * qFabs(yRange.size());

	xAxis->setRange(xRange);
	yAxis->setRange(yRange);
	graph(graphIndex)->setData(key, value);
}

/* 鼠标双击事件 */
void QLsCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
{
	/* 触发双击事件则关闭单击事件的定时器 */
	m_clickedTimer->stop();

	/* 获取原坐标信息 */
	double xcenter = xAxis->pixelToCoord(event->pos().x());
	double ycenter = yAxis->pixelToCoord(event->pos().y());
	double xsize = xAxis->range().size();
	double ysize = yAxis->range().size();
	double x1, y1, x2, y2;

	/* 双击左键，放大 */
	if(event->buttons() == Qt::LeftButton)
	{
		xsize *= 0.5;
		ysize *= 0.5;
	}
	/* 双击右键，缩小 */
	else if(event->buttons() == Qt::RightButton)
	{
		xsize *= 2.0;
		ysize *= 2.0;
	}

	/* 设置缩放后的范围 */
	x1 = xcenter - 0.5*xsize;
	y1 = ycenter - 0.5*ysize;
	x2 = xcenter + 0.5*xsize;
	y2 = ycenter + 0.5*ysize;
	xAxis->setRange(x1, x2);
	yAxis->setRange(y1, y2);
	replot();
}

/* 鼠标按下事件 */
void QLsCustomPlot::mousePressEvent(QMouseEvent *event)
{
	/* 缩放 */
	if(mZoomMode)
	{
		/* 右键选择缩放区域 */
		if(event->buttons() == Qt::RightButton)
		{
			mOrigin = event->pos();
		}
	}

	/* 记录触发单击的鼠标事件，启动单击事件定时器，区分单双击 */
	m_clickedMouseButton = event->buttons();
	m_clickedTimer->start(200);

	QCustomPlot::mousePressEvent(event);
}

/* 鼠标移动事件 */
void QLsCustomPlot::mouseMoveEvent(QMouseEvent *event)
{
	/* 如果处于右键选择区域状态，则更新区域 */
	if(mRubberBand->isVisible())
	{
		mRubberBand->setGeometry(QRect(mOrigin, event->pos()).normalized());
	}

	/* 光标划过曲线显示点的坐标 */
	ShowPoint(event);

	QCustomPlot::mouseMoveEvent(event);
}

/* 鼠标释放事件 */
void QLsCustomPlot::mouseReleaseEvent(QMouseEvent *event)
{
	/* 右键单击，不触发区域选择 */
	if(event->button() == Qt::RightButton)
	{
		m_clickedMouseButton = Qt::NoButton;
	}

	do
	{
		/* 如果处于右键绘图状态，且松开右键，则缩放至选中区域 */
		if (mRubberBand->isVisible() && event->button() == Qt::RightButton)
		{
			/* 获取缩放区域 */
			const QRect zoomRect = mRubberBand->geometry();
			int xp1, yp1, xp2, yp2;
			zoomRect.getCoords(&xp1, &yp1, &xp2, &yp2);

			/* 区域缩放范围，太小忽略 */
			if(qAbs(xp1 - xp2) < 2)
			{
				mRubberBand->hide();
				break;
			}

			/* 设置缩放后的范围 */
			double x1 = xAxis->pixelToCoord(xp1);
			double x2 = xAxis->pixelToCoord(xp2);
			double y1 = yAxis->pixelToCoord(yp1);
			double y2 = yAxis->pixelToCoord(yp2);
			xAxis->setRange(x1, x2);
			yAxis->setRange(y1, y2);

			/* 设置结束，隐藏绘制区域 */
			mRubberBand->hide();
			replot();
		}
	} while(0);

	QCustomPlot::mouseReleaseEvent(event);
}

/* 尺寸变化事件重载 */
void QLsCustomPlot::resizeEvent(QResizeEvent *event)
{
	do
	{
		/* 背景图片变化 */
		if(!m_bgImg) break;

		/* 背景图片显示尺寸变化 */
		m_bgShowSize.setWidth(size().width() * m_bgXCPRange.size() / m_bgSize.width());
		m_bgShowSize.setHeight(size().height() * m_bgYCPRange.size() / m_bgSize.height());

		/* 背景图片缩放 */
		BgResize();

		replot();
	} while(0);

	QCustomPlot::resizeEvent(event);
}

/* 图形初始化 */
void QLsCustomPlot::PlotInit()
{
	/*
	 * QCP::iRangeDrag：可鼠标拖动
	 * QCP::iRangeZoom：可滚轮缩放
	 * QCP::iSelectPlottables：图标可选择
	 */
	setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

	legend->setVisible(true);					/* 设定右上角图形标注可见 */
	legend->setFont(QFont("Helvetica", 9));		/* 设定右上角图形标注的字体 */
	axisRect()->setupFullAxesBox();				/* 在坐标轴右侧和上方画线，和X/Y轴一起形成一个矩形 */
	setNotAntialiasedElements(QCP::aeAll);		/* 禁用反锯齿，提升效率 */
	setSelectionTolerance(1);					/* 确定鼠标点击的精度 */

	/* 设置坐标背景色 */
	if(m_bgImg)
		axisRect()->setBackground(QPixmap::fromImage(*m_bgImg),true,Qt::IgnoreAspectRatio);

	qRegisterMetaType<QCPRange>("QCPRange");
	connect(xAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(slotXRangeChanged(QCPRange)));
	connect(yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(slotYRangeChanged(QCPRange)));

	replot();
}

/* 背景图片缩放 */
void QLsCustomPlot::BgResize()
{
	/* 没有背景图片则跳过 */
	if(!m_bgImg) return;

	/* 背景图片区域 */
	double xx1 = (xAxis->range().lower - m_bgXCPRange.lower) * m_bgSize.width() / m_bgXCPRange.size();
	double yy1 = (m_bgYCPRange.upper - yAxis->range().upper) * m_bgSize.height() / m_bgYCPRange.size();
	double xx2 = (xAxis->range().upper - m_bgXCPRange.lower) * m_bgSize.width() / m_bgXCPRange.size();
	double yy2 = (m_bgYCPRange.upper - yAxis->range().lower) * m_bgSize.height() / m_bgYCPRange.size();

	/* 设置背景图片 */
	axisRect()->setBackground(
				QPixmap::fromImage(
					m_bgImg->copy(
						QRect(QPoint(xx1, yy1), QPoint(xx2, yy2))
						).scaled(
						m_bgShowSize.width(), m_bgShowSize.height(),
						Qt::KeepAspectRatio, Qt::SmoothTransformation
						)
					)
				);
	replot();
}

/* 光标划过曲线显示点的坐标 */
void QLsCustomPlot::ShowPoint(QMouseEvent *event)
{
	if (!m_bShowPoint || graphCount() == 0)
		return;

	//获取鼠标坐标，相对父窗体坐标
	int x_pos = event->pos().x();
	int y_pos = event->pos().y();

	//鼠标坐标转化为CustomPlot内部坐标
	float x_val = xAxis->pixelToCoord(x_pos);
	float y_val = yAxis->pixelToCoord(y_pos);

	//通过坐标轴范围判断光标是否在点附近
	float x_begin = xAxis->range().lower;
	float x_end = xAxis->range().upper;
	float y_begin = yAxis->range().lower;
	float y_end = yAxis->range().upper;
	float x_tolerate = (x_end - x_begin) / 40;//光标与最近点距离在此范围内，便显示该最近点的值
	float y_tolerate = (y_end - y_begin) / 40;

	//判断有没有超出坐标轴范围
	if (x_val < x_begin || x_val > x_end)
		return;

	//通过x值查找离曲线最近的一个key值索引
	int index = 0;
	int index_left = graph(0)->findBegin(x_val, true);//左边最近的一个key值索引
	int index_right = graph(0)->findEnd(x_val, true);//右边
	float dif_left = abs(graph(0)->data()->at(index_left)->key - x_val);
	float dif_right = abs(graph(0)->data()->at(index_right)->key - x_val);
	if (dif_left < dif_right)
		index = index_left;
	else
		index = index_right;

	x_val = graph(0)->data()->at(index)->key;//通过得到的索引获取key值

	int graphIndex=0;//curve index closest to the cursor
	float dx = abs(x_val - xAxis->pixelToCoord(x_pos));
	float dy= abs(graph(0)->data()->at(index)->value - y_val);

	//通过遍历每条曲线在index处的value值，得到离光标点最近的value及对应曲线索引
	for (int i = 0; i < xAxis->graphs().count(); i++)
	{
		if (abs(graph(i)->data()->at(index)->value - y_val)<dy)
		{
			dy = abs(graph(i)->data()->at(index)->value - y_val);
			graphIndex = i;
		}
	}

	QString strToolTip = "测量点" + QString::number(graphIndex + 1) + ": (";
	strToolTip += QString::number(x_val, 10, 4) + ", ";
	strToolTip += QString::number(graph(graphIndex)->data()->at(index)->value) + ")";

	//判断光标点与最近点的距离是否在设定范围内
	if (dy < y_tolerate && dx < x_tolerate)
		QToolTip::showText(cursor().pos(), strToolTip, this);
}

/* 单击事件槽函数 */
void QLsCustomPlot::slotClicked()
{
	/* 停止单击事件的定时器 */
	m_clickedTimer->stop();

	/* 设置缩放 */
	if (mZoomMode)
	{
		/* 缩放右键选择区域 */
		if (m_clickedMouseButton == Qt::RightButton)
		{
			mRubberBand->setGeometry(QRect(mOrigin, QSize(0, 0)));
			mRubberBand->show();
			emit sigReportHasZoom();
		}
	}
}

/* 轴变化的槽函数 */
void QLsCustomPlot::slotXRangeChanged(const QCPRange &newRange)
{
	/* 没有背景图片则跳过 */
	if(!m_bgImg) return;

	/* 防止x轴超出范围 */
	if(newRange.lower < m_bgXCPRange.lower || newRange.upper > m_bgXCPRange.upper)
	{
		xAxis->setRange(qMax(newRange.lower, m_bgXCPRange.lower),
						qMax(newRange.upper, m_bgXCPRange.upper));
	}

	/* 背景图片缩放 */
	BgResize();
}
void QLsCustomPlot::slotYRangeChanged(const QCPRange &newRange)
{
	/* 没有背景图片则跳过 */
	if(!m_bgImg) return;

	/* 防止y轴超出范围 */
	if(newRange.lower < m_bgYCPRange.lower || newRange.upper > m_bgYCPRange.upper)
	{
		yAxis->setRange(qMax(newRange.lower, m_bgYCPRange.lower),
						qMax(newRange.upper, m_bgYCPRange.upper));
	}

	/* 背景图片缩放 */
	BgResize();
}

/* 光标划过曲线显示点的坐标 */
void QLsCustomPlot::setBShowPoint(bool bShowPoint)
{
	m_bShowPoint = bShowPoint;
}

/* 背景图片 */
QImage *QLsCustomPlot::bgImg() const
{
	return m_bgImg;
}
void QLsCustomPlot::setBgImg(QImage *bgImg)
{
	m_bgImg = bgImg;
}

/* 背景图片y的范围 */
QCPRange QLsCustomPlot::bgYCPRange() const
{
	return m_bgYCPRange;
}
void QLsCustomPlot::setBgYCPRange(const QCPRange &bgYCPRange)
{
	m_bgYCPRange = bgYCPRange;
}

/* 背景图片x的范围 */
QCPRange QLsCustomPlot::bgXCPRange() const
{
	return m_bgXCPRange;
}
void QLsCustomPlot::setBgXCPRange(const QCPRange &bgXCPRange)
{
	m_bgXCPRange = bgXCPRange;
}

/* 背景图片的尺寸 */
QSize QLsCustomPlot::bgSize() const
{
	return m_bgSize;
}
void QLsCustomPlot::setBgSize(const QSize &bgSize)
{
	m_bgSize = bgSize;
}

/* 缩放标识 */
void QLsCustomPlot::setZoomMode(bool zoomMode)
{
	mZoomMode = zoomMode;
}
